package wh.coach.pivot3.table;

import lombok.AllArgsConstructor;
import wh.coach.domain.CalType;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * @author xhw
 * @date 2024/1/20
 */

@AllArgsConstructor
public class Aggregation {
    private final CalType calType;
    private final List<Number> values;

    public Number calculate() {
        switch (calType) {
            case SUM:
                return calculateSum();
            case AVG:
                return calculateAvg();
            case MAX:
                return calculateMax();
            case MIN:
                return calculateMin();
            case MEDIAN:
                return calculateMedian();
            case COUNT:
                return calculateCount();
            default:
                return null;
        }
    }

    private Number calculateSum() {
        if (isNullOrEmpty(values)) {
            return null;
        }
        double sum = 0;
        for (Number value : values) {
            sum += value.doubleValue();
        }
        return sum;
    }


    private Number calculateAvg() {
        if (isNullOrEmpty(values)) {
            return null;
        }
        double sum = 0;
        for (Number value : values) {
            sum += value.doubleValue();
        }
        return sum / values.size();
    }

    private Number calculateCount() {
        if (isNullOrEmpty(values)) {
            return null;
        }
        return values.size();
    }

    private Number calculateMedian() {
        if (isNullOrEmpty(values)) {
            return null;
        }
        List<Number> sortedValues = new ArrayList<>(values);
        sortedValues.sort(new Comparator<Number>() {
            @Override
            public int compare(Number o1, Number o2) {
                return Double.compare(o1.doubleValue(), o2.doubleValue());
            }
        });
        int size = sortedValues.size();
        if (size % 2 == 0) {
            int midIndex1 = size / 2 - 1;
            int midIndex2 = size / 2;
            double median = (sortedValues.get(midIndex1).doubleValue() + sortedValues.get(midIndex2).doubleValue()) / 2;
            return median;
        } else {
            int midIndex = size / 2;
            return sortedValues.get(midIndex).doubleValue();
        }
    }

    private Number calculateMin() {
        if (isNullOrEmpty(values)) {
            return null;
        }
        Number min = values.get(0);
        for (Number value : values) {
            if (value.doubleValue() < min.doubleValue()) {
                min = value;
            }
        }
        return min;
    }

    private Number calculateMax() {
        if (isNullOrEmpty(values)) {
            return null;
        }
        Number max = values.get(0);
        for (Number value : values) {
            if (value.doubleValue() > max.doubleValue()) {
                max = value;
            }
        }
        return max;
    }

    private boolean isNullOrEmpty(List<Number> values) {
        return values == null || values.isEmpty();
    }
}
